﻿//Process Memory Class
//Read, Write Process Memory
//Programmiert smupwn
//Copyright 2013-2014 by smupwn & lacka
//Project Robotic One

using System;
using System.Text;
using System.Security;
using System.Reflection;
using System.Diagnostics;
using System.Security.Principal;
using System.Collections.Generic;
using System.Runtime.InteropServices;


namespace robotic_one.Helper
{
    
    class Memory
    {
        #region "DLL Import"
        [DllImport("kernel32.dll")]
        public static extern bool ReadProcessMemory(IntPtr hProcess, uint lpBaseAddress, [In, Out] byte[] buffer, int size, out int lpNumberOfBytesRead);

        [DllImport("kernel32.dll")]
        public static extern Int32 ReadProcessMemory(IntPtr Process, IntPtr BaseAddress, [In(), Out()]byte[] buffer, UInt32 size, IntPtr NumBytesRead);

        [DllImport("kernel32", EntryPoint = "ReadProcessMemory")]
        public static extern bool ReadProcessMemory(IntPtr hProcess, uint dwAddress, IntPtr lpBuffer, int nSize, out int lpBytesRead);
        
        [DllImport("kernel32.dll")]
        public static extern bool WriteProcessMemory(IntPtr hProcess, uint lpBaseAddress, [In, Out] byte[] buffer, int size, out int lbNumOfBytesWrite);

        [DllImport("kernel32.dll")]
        public static extern IntPtr OpenProcess(uint dwAccess, bool bInheritHandle, int dwProcessId);

        [DllImport("kernel32")]
        public static extern bool CloseHandle(IntPtr hHandle);

        //[DllImport("User32.dll")]
        //private static bool SetForegroundWindow(IntPtr hWnd);
        #endregion

        string GameProcess = "Wow";
        private static Process ProcessObject;
        public static IntPtr ProcessHandle { get; private set; }
        private static readonly UTF8Encoding UTF8 = new UTF8Encoding();

        public static bool Initialized
        {
            get { return ProcessHandle != IntPtr.Zero; }
        }

        private static Process ProcessId
        {
            get { return ProcessObject; }
            set { ProcessObject = value; }
        }

        public static IntPtr WindowHandle
        {
            get { return ProcessObject.MainWindowHandle; }
        }

        public static ProcessModule MainModule
        {
            get
            {
                try
                {
                    return ProcessObject.MainModule;
                }
                catch
                {
                }
                return null;
            }
        }

        public static uint BaseAddress
        {
            get { return Convert.ToUInt32(MainModule.BaseAddress); }
        }

        public IntPtr OpenProcess()
        {
            if (System.Diagnostics.Process.GetProcessesByName(GameProcess).Length == 1)
            {
                return System.Diagnostics.Process.GetProcessesByName(GameProcess)[0].Handle;
            }
            return new IntPtr(0);
        }

        //public static void CloseProcess()
        //{
        //    CloseHandle(ProcessHandle);
        //    Process.LeaveDebugMode();
        //}

        public bool _ProcessHandle(string ProcessName)
        {
            GameProcess = ProcessName;
            if (System.Diagnostics.Process.GetProcessesByName(ProcessName).Length == 1)
            {
                return true;
            }
            return false;
        }

        //public static void setForeground()
        //{
        //    int ProcessId = 0;
        //    Process p = System.Diagnostics.Process.GetProcessById(ProcessId);
        //    SetForegroundWindow(p.MainWindowHandle);
        //}

        #region "Read"
        public static object ReadObject(uint dwAddress, Type objType)
        {
            IntPtr lpBuffer = IntPtr.Zero;
            int iBytesRead;
            int iObjectSize;
            object objRet;

            try
            {
                iObjectSize = Marshal.SizeOf(objType);
                lpBuffer = Marshal.AllocHGlobal(iObjectSize);

                iBytesRead = ReadRawMemory(ProcessHandle, dwAddress, lpBuffer, iObjectSize);
                if (iBytesRead != iObjectSize)
                    throw new Exception("ReadProcessMemory error in ReadObject.");

                objRet = Marshal.PtrToStructure(lpBuffer, objType);
            }
            catch
            {
                return null;
            }
            finally
            {
                if (lpBuffer != IntPtr.Zero)
                    Marshal.FreeHGlobal(lpBuffer);
            }

            return objRet;
        }

        public byte[] ReadBytes(int Address, int BytesLength)
        {
            byte[] buffer = new byte[BytesLength];
            IntPtr R = default(IntPtr);
            ReadProcessMemory(OpenProcess(), new IntPtr(Address), buffer, Convert.ToUInt32(buffer.Length), R);
            return buffer;
        }

        public static byte[] ReadBytes(uint address, int count)
        {
            var ret = new byte[count];
            int numRead;
            if (ReadProcessMemory(ProcessHandle, address, ret, count, out numRead) && numRead == count)
            {
                return ret;
            }
            return null;
        }

        public static byte[] ReadBytes(IntPtr hProcess, uint dwAddress, int nSize)
        {
            IntPtr lpBuffer = IntPtr.Zero;
            int iBytesRead;
            byte[] baRet;

            try
            {
                lpBuffer = Marshal.AllocHGlobal(nSize);

                iBytesRead = ReadRawMemory(hProcess, dwAddress, lpBuffer, nSize);
                if (iBytesRead != nSize)
                    throw new Exception("ReadProcessMemory error in ReadBytes");

                baRet = new byte[iBytesRead];
                Marshal.Copy(lpBuffer, baRet, 0, iBytesRead);
            }
            catch
            {
                return null;
            }
            finally
            {
                if (lpBuffer != IntPtr.Zero)
                    Marshal.FreeHGlobal(lpBuffer);
            }

            return baRet;
        }

        public static int ReadRawMemory(IntPtr hProcess, uint dwAddress, IntPtr lpBuffer, int nSize)
        {
            try
            {
                int lpBytesRead;
                if (!ReadProcessMemory(hProcess, dwAddress, lpBuffer, nSize, out lpBytesRead))
                    throw new Exception("ReadProcessMemory failed");

                return lpBytesRead;
            }
            catch
            {
                return 0;
            }
        }

        public static T ReadStruct<T>(uint address) where T : struct
        {
            IntPtr localStore = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(T)));
            Marshal.Copy(ReadBytes(address, Marshal.SizeOf(typeof(T))), 0, localStore, Marshal.SizeOf(typeof(T)));
            var ret = (T)Marshal.PtrToStructure(localStore, typeof(T));
            Marshal.FreeHGlobal(localStore);
            return ret;
        }

        public static T ReadStruct<T>(IntPtr address) where T : struct
        {
            IntPtr localStore = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(T)));
            Marshal.Copy(ReadBytes((uint)address, Marshal.SizeOf(typeof(T))), 0, localStore, Marshal.SizeOf(typeof(T)));
            var ret = (T)Marshal.PtrToStructure(localStore, typeof(T));
            Marshal.FreeHGlobal(localStore);
            return ret;
        }

        private static T ReadInternal<T>(uint address)
        {
            object ret;
            try
            {
                if (typeof(T) == typeof(string))
                {
                    var chars = new List<char>();
                    uint offset = 0;
                    char lastChar;
                    while ((lastChar = (char)Read<byte>(address + offset)) != '\0')
                    {
                        offset++;
                        chars.Add(lastChar);
                    }
                    ret = new string(chars.ToArray());

                    return (T)ret;
                }

                int numBytes = Marshal.SizeOf(typeof(T));
                if (typeof(T) == typeof(IntPtr))
                {
                    ret = (IntPtr)BitConverter.ToInt64(ReadBytes(address, numBytes), 0);
                    return (T)ret;
                }

                switch (Type.GetTypeCode(typeof(T)))
                {
                    case TypeCode.Boolean:
                        ret = ReadBytes(address, 1)[0] != 0;
                        break;
                    case TypeCode.Char:
                        ret = (char)ReadBytes(address, 1)[0];
                        break;
                    case TypeCode.SByte:
                        ret = (sbyte)ReadBytes(address, numBytes)[0];
                        break;
                    case TypeCode.Byte:
                        ret = ReadBytes(address, numBytes)[0];
                        break;
                    case TypeCode.Int16:
                        ret = BitConverter.ToInt16(ReadBytes(address, numBytes), 0);
                        break;
                    case TypeCode.UInt16:
                        ret = BitConverter.ToUInt16(ReadBytes(address, numBytes), 0);
                        break;
                    case TypeCode.Int32:
                        ret = BitConverter.ToInt32(ReadBytes(address, numBytes), 0);
                        break;
                    case TypeCode.UInt32:
                        ret = BitConverter.ToUInt32(ReadBytes(address, numBytes), 0);
                        break;
                    case TypeCode.Int64:
                        ret = BitConverter.ToInt64(ReadBytes(address, numBytes), 0);
                        break;
                    case TypeCode.UInt64:
                        ret = BitConverter.ToUInt64(ReadBytes(address, numBytes), 0);
                        break;
                    case TypeCode.Single:
                        ret = BitConverter.ToSingle(ReadBytes(address, numBytes), 0);
                        break;
                    case TypeCode.Double:
                        ret = BitConverter.ToDouble(ReadBytes(address, numBytes), 0);
                        break;
                    default:
                        IntPtr dataStore = Marshal.AllocHGlobal(numBytes);
                        byte[] data = ReadBytes(address, numBytes);
                        Marshal.Copy(data, 0, dataStore, numBytes);
                        ret = Marshal.PtrToStructure(dataStore, typeof(T));
                        Marshal.FreeHGlobal(dataStore);
                        break;
                }
                return (T)ret;
            }
            catch
            {
                return default(T);
            }
        }
        
        public int ReadInt(int Address)
        {
            byte[] buffer = new byte[4];
            IntPtr R = default(IntPtr);
            ReadProcessMemory(OpenProcess(), new IntPtr(Address), buffer, Convert.ToUInt32(4), R);
            return BitConverter.ToInt32(buffer, 0);
        }

        public uint ReadUInt(uint Address)
        {
            byte[] buffer = new byte[4];
            IntPtr R = default(IntPtr);
            ReadProcessMemory(OpenProcess(), new IntPtr(Address), buffer, Convert.ToUInt32(4), R);
            return BitConverter.ToUInt32(buffer, 0);
        }

        public static T Read<T>(params uint[] addresses)
        {
            if (addresses.Length == 0)
            {
                return default(T);
            }
            if (addresses.Length == 1)
            {
                return ReadInternal<T>(addresses[0]);
            }

            uint last = 0;
            for (int i = 0; i < addresses.Length; i++)
            {
                if (i == addresses.Length - 1)
                {
                    return ReadInternal<T>(addresses[i] + last);
                }
                last = ReadInternal<uint>(last + addresses[i]);
            }

            return default(T);
        }

        public static T Read<T>(params IntPtr[] addresses)
        {
            if (addresses.Length == 0)
            {
                return default(T);
            }
            if (addresses.Length == 1)
            {
                return ReadInternal<T>((uint)addresses[0]);
            }
            uint last = 0;
            for (int i = 0; i < addresses.Length; i++)
            {
                if (i == addresses.Length - 1)
                {
                    return ReadInternal<T>((uint)addresses[i] + last);
                }
                last = ReadInternal<uint>((uint)last + (uint)addresses[i]);
            }
            return default(T);
        }

        public string ReadString(uint Address, int Length)
        {
            byte[] buffer = new byte[Length];
            IntPtr R = default(IntPtr);
            ReadProcessMemory(OpenProcess(), new IntPtr(Address), buffer, Convert.ToUInt32(buffer.Length), R);
            return new System.Text.ASCIIEncoding().GetString(buffer, 0, Length);
        }

        public static string ReadUtf8(uint dwAddress, int Size)
        {
            byte[] l = ReadBytes(ProcessHandle, dwAddress, Size);

            if (l == null)
                return String.Empty;

            return UTF8Decoder(l);
        }

        private static string UTF8Decoder(byte[] bytes)
        {
            String _s = UTF8.GetString(bytes, 0, bytes.Length);
            if (_s.IndexOf("\0") != -1)
                _s = _s.Remove(_s.IndexOf("\0"), _s.Length - _s.IndexOf("\0"));
            return _s;
        }

        public float ReadFloat(uint Address)
        {
            byte[] buffer = new byte[8];
            IntPtr R = default(IntPtr);
            ReadProcessMemory(OpenProcess(), new IntPtr(Address), buffer, Convert.ToUInt32(4), R);
            return BitConverter.ToSingle(buffer, 0);
        }

        #endregion




    }
}
